#include <GL/glut.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include "camera.h"

// This is our global camera object
CCamera Camera;
int   last_x, last_y;
bool	fullscreen = false;	// Fullscreen Flag Set To Fullscreen Mode By Default
int main_window;
float angle=0.0,deltaStrafe = 0.0,deltaMove = 0,ratio;
int width = 0, height = 0;

// Init callback
void init (void)
{
   float pos[3] = {0.0f, 1.0f, 5.0f};
   glClearColor (0.0, 0.0, 0.0, 0.0);
   glShadeModel (GL_SMOOTH);
   glEnable(GL_DEPTH_TEST);               // Habilita Z-buffer
   glEnable(GL_LIGHTING);                 // Habilita luz
   glEnable(GL_LIGHT0);                   // habilita luz 0

	// Cor da fonte de luz (RGBA)
	GLfloat cor_luz[]         = { 1.0, 1.0, 1.0, 1.0};
	// Posicao da fonte de luz. Ultimo parametro define se a luz sera direcional (0.0) ou tera uma posicional (1.0)
	GLfloat posicao_luz[]     = { 50.0, 50.0, 50.0, 1.0};

   // Define parametros da luz
   glLightfv(GL_LIGHT0, GL_AMBIENT, cor_luz);
   glLightfv(GL_LIGHT0, GL_DIFFUSE, cor_luz);
   glLightfv(GL_LIGHT0, GL_SPECULAR, cor_luz);
   glLightfv(GL_LIGHT0, GL_POSITION, posicao_luz);

   Camera.PositionCamera(pos[0],pos[1],pos[2],
                         5.0f,  1.0f,  5.0f,
                         0.0f,  1.0f,  0.0f);
}

// Keyboard callback
void keyboard(unsigned char key, int x, int y)
{
  switch (tolower(key))
  {
      case 'a':  deltaStrafe	= -0.1f;    break;
      case 'd' : deltaStrafe 	=  0.1f;    break;
      case 'w' : deltaMove 	=  0.1f;    break;
      case 's' : deltaMove 	= -0.1f;    break;
		case 27: exit(0);							break;
  }
}

void realeasekeyboard(unsigned char key, int x, int y)
{
	switch (key)
   {
		case 'a':
		case 'd' : deltaStrafe = 0.0f; break;
		case 'w' :
		case 's' : deltaMove = 0.0f;   break;
	}
}

void setMaterial(void)
{
	// Material do objeto (neste caso, ruby). Parametros em RGBA
	GLfloat objeto_ambient[]   = { .1745, .01175, .01175, 1.0 };
	GLfloat objeto_difusa[]    = { .91424, .04136, .04136, 1.0 };
	GLfloat objeto_especular[] = { .727811, .626959, .626959, 1.0 };
	GLfloat objeto_brilho[]    = { 90.0f };

   // Define os parametros da superficie a ser iluminada
   glMaterialfv(GL_FRONT, GL_AMBIENT, objeto_ambient);
   glMaterialfv(GL_FRONT, GL_DIFFUSE, objeto_difusa);
   glMaterialfv(GL_FRONT, GL_SPECULAR, objeto_especular);
   glMaterialfv(GL_FRONT, GL_SHININESS, objeto_brilho);
}

// Draw the scene
void desenhaCena()
{
   float size = 100.0f;
   glDisable(GL_LIGHTING);
   glColor3f(0.9f, 0.9f, 0.9f);
   glPushMatrix();
      glScalef(size, .1, size);
      glutSolidCube(1);
   glPopMatrix();
   glColor3f(0.8f, 1.0f, 0.8f);
   glPushMatrix();
      glTranslatef(0.0f, 8.0f, 0.0f);
      glScalef(size, .1, size);
      glutSolidCube(1);
   glPopMatrix();

   glEnable(GL_LIGHTING);
   setMaterial();
   for(int j = -50; j <= 50; j+=10)
   {
      for(int i = -50; i <= 50; i+=10)
      {
      glPushMatrix();
         glTranslated(i, 4, j);
         glScalef(1.5, 8, 1.5);
         glutSolidCube(1);
      glPopMatrix();
      }
   }
   glDisable(GL_LIGHTING);
}

// Idle Function
void idle( void )
{
   if(glutGetWindow() != main_window)
      glutSetWindow(main_window);
  // Update Camera
  Camera.Update();
  glutPostRedisplay();
}

// Reshape callback
void reshape (int w, int h)
{
   width  = w;
   height = h;
	if (height==0) height=1;	       				// Prevent A Divide By Zero By
	glViewport(0,0,width,height);						// Reset The Current Viewport
	Camera.SetWindowSize(width, height);

	glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
	glLoadIdentity();									   // Reset The Projection Matrix
	gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,500.0f);	// Calculate The Aspect Ratio Of The Window
}

// Display function
void display(void)
{
   glClear (GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);						// Select The Modelview Matrix
	glLoadIdentity();

	if (deltaMove)   Camera.MoveCamera(deltaMove);
	if (deltaStrafe) Camera.StrafeCamera(deltaStrafe);

   Camera.Look();
   float lightpos[3];
   Camera.GetPosition(lightpos);

   desenhaCena();

	glutSwapBuffers();
}

void pressKey(int key, int x, int y)
{
   switch (key)
   {
      case GLUT_KEY_LEFT :  deltaStrafe = -0.1f;       break;
      case GLUT_KEY_RIGHT : deltaStrafe = 0.1f;        break;
      case GLUT_KEY_UP :    deltaMove = 0.1f;           break;
      case GLUT_KEY_DOWN :  deltaMove = -0.1f;          break;
      case GLUT_KEY_F11 :
         fullscreen = !fullscreen;
         (fullscreen) ? glutFullScreen() : glutReshapeWindow(800,600);
      break;
	}
}

void releaseKey(int key, int x, int y)
{
	switch (key)
   {
		case GLUT_KEY_LEFT :
		case GLUT_KEY_RIGHT : deltaStrafe = 0.0f;break;
		case GLUT_KEY_UP :
		case GLUT_KEY_DOWN : deltaMove = 0.0f;break;
	}
}

/*
void pmotion(int x, int y )
{
  Camera.SetCursorPosition( last_x - x, last_y - y);

  last_x = x;
  last_y = y;

  glutPostRedisplay();
}
*/

void motion(int x, int y )
{
  Camera.SetCursorPosition( last_x - x, last_y - y);

  last_x = x;
  last_y = y;

  glutPostRedisplay();
}

// Manipula os eventos de clique do mouse
void mouse(int button, int state, int x, int y)
{
  	if ( button == GLUT_LEFT_BUTTON && state == GLUT_DOWN )
	{
   	last_x = x;
    	last_y = y;
  	}
}

// Main
int main(int argc, char** argv)
{
   glutInit(&argc, argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
   glutInitWindowSize (800,600);
   glutInitWindowPosition (10, 10);
   main_window = glutCreateWindow ("Navigator - F11 for FULLSCREEN");
	glutSetCursor( GLUT_CURSOR_INFO); // Para tirar o cursor, usar GLUT_CURSOR_NONE

   init ();

   glutIdleFunc( idle );
   glutMouseFunc( mouse );
   //glutPassiveMotionFunc( pmotion );
   glutMotionFunc( motion );
	glutIgnoreKeyRepeat(1);
	glutSpecialFunc(pressKey);
	glutSpecialUpFunc(releaseKey);
   glutKeyboardFunc( keyboard );
   glutKeyboardUpFunc( realeasekeyboard );
	glutReshapeFunc( reshape );
   glutDisplayFunc(display);

   glutMainLoop();
   return 0;
}
